#include <fstream>
#include <stdexcept>
#include <sstream>
#include "parser.h"
#include "tokenizer.h"
#include "frontend.h"

using StructRefMap = std::unordered_map<std::string, std::list<std::string>>;
static StructRefMap refMap;


std::string Parser::workingPath_ = "";
std::vector<std::string> Parser::includePathlist_;

std::string Parser::getTokenError(Token& token, std::ostream& os) {
    std::stringstream ss;
    ss << "-> " << token.getFile() << " ( " << token.getLineno() << ","
       << token.getCol() << " ): ";
    return ss.str();
}

Parser::~Parser() {
    for(auto* tokenizer : tokenizerList_) {
        delete tokenizer;
    }
    tokenizerList_.clear();
}

bool Parser::analyze(const std::string& file, std::ostream& os, const std::string& searchpath) {
    Tokenizer* tokenizer = new Tokenizer(file);
    tokenizerList_.push_back(tokenizer);
    curTokenizer_ = tokenizer;
    std::stringstream ss;
    curFile_ = file;
    try {
        return parse(file, *tokenizer, os, searchpath);
    } catch (std::runtime_error& rex) {
        ss << getTokenError(tokenizer->getCurToken(), os) << rex.what();
        tokenizer->printLine(tokenizer->getCurToken(), ss);
        error_ = ss.str();
        os << error_;
        return false;
    } catch (std::exception& ex) {
        ss << getTokenError(tokenizer->getCurToken(), os) << ex.what();
        tokenizer->printLine(tokenizer->getCurToken(), ss);
        error_ = ss.str();
        os << error_;
        return false;
    } catch (...) {
        return false;
    }
    return true;
}

bool Parser::doChunk(std::istream& is, Tokenizer& tokenizer, std::ostream& os,
    bool raiseError) {
    auto& token = tokenizer.getNext(is);
    if (!token) {
        return true;
    }
    switch (token.getType()) {
        case TokenType::KEYWORD_STRUCT:
            doStruct(is, tokenizer, os);
            break;
        case TokenType::KEYWORD_SERVICE:
            doService(is, tokenizer, os);
            break;
        case TokenType::TERM_DOUBLE_SLASH:
            doSingleComment(is, tokenizer);
            break;
        case TokenType::TERM_COMMENT_BEGIN:
            doMultiComment(is, tokenizer);
            break;
        case TokenType::TERM_HASH:
            doImport(is, tokenizer, os);
            break;
        case TokenType::TERM_BLANK:
            break;
        default:
            if (raiseError) {
                throw std::runtime_error("Error, unknown identifier");
            }
            return false;
    }
    return true;
}

std::string ConvertPath(const std::string& filedir) {
    char dir[1024] = {};

#ifdef _WIN32
    char* res = _fullpath(dir, filedir.c_str(), 1024);
    if  (res == nullptr) {
        return "";
    }
#else
    realpath(filedir.c_str(), dir);
#endif
    return dir;
}

bool Parser::parse(const std::string& file, Tokenizer& tokenizer,
    std::ostream& os, 
    const std::string& searchpath) {
    std::fstream fs;
    std::string fullpath = searchpath + "/" + file;
    fs.open(fullpath, std::ios::in);
    if (!fs) {
        //error_ = "File not found:" + ConvertPath(fullpath);
        //阶段搜索不算失败
        //os << "File not found:" + ConvertPath(fullpath) << std::endl;
        return false;
    }
    fileSet_.emplace(file);
    while (!fs.fail()) {
        if (!doChunk(fs, tokenizer, os, true)) {
            return false;
        }
    }
    return true;
}

bool isContKey(ValueType type) {
    switch (type) {
        case ValueType::STRING:
        case ValueType::I8:
        case ValueType::I16:
        case ValueType::I32:
        case ValueType::I64:
        case ValueType::UI8:
        case ValueType::UI16:
        case ValueType::UI32:
        case ValueType::UI64:
            return true;
        default:
            break;
    }
    return false;
}

bool Parser::validateStructField(std::ostream& os, IdlNode& field, std::list<std::string>& ref) {
    if (field.type == ValueType::DICT
        && field.second->type == ValueType::STRUCT) {
        // dict<x, target>
        auto& token = field.second->token;
        if (structSet_.find(token.getText()) == structSet_.end()) {
            os << getTokenError(token, os) << "User defined type: "
               << token.getText() << " not found";
            token.printLine(os);
            os << std::endl;
            validationFailed_ = true;
        } else {
            ref.push_back(token.getText());
        }
    } else if (field.type == ValueType::STRUCT) {
        auto& token = field.token;
        if (structSet_.find(token.getText()) == structSet_.end()) {
            os << getTokenError(token, os) << "User defined type: "
               << token.getText() << " not found";
            token.printLine(os);
            os << std::endl;
            validationFailed_ = true;
        } else {
            ref.push_back(token.getText());
        }
    } else if (field.type == ValueType::SEQ
               && field.first->type == ValueType::STRUCT) {
        auto& token = field.first->token;
        if (structSet_.find(token.getText()) == structSet_.end()) {
            os << getTokenError(token, os) << "User defined type: "
               << token.getText() << " not found";
            token.printLine(os);
            os << std::endl;
            validationFailed_ = true;
        } else {
            ref.push_back(token.getText());
        }
    }
    return true;
}

bool Parser::Parser::validateAllStruct(std::ostream& os) {
    for (auto& stru : structList_) {
        for (auto& field : stru.field_list) {
            validateStructField(os, field, refMap[stru.name]);
        }
    }
    // TODO REF MAP
    return true;
}

bool Parser::validateMethodArgument(std::ostream& os, IdlNode& arg) {
    if (arg.type == ValueType::STRUCT) {
        auto& token = arg.token;
        if (structSet_.find(token.getText()) == structSet_.end()) {
            os << getTokenError(token, os) << "User defined type: "
               << token.getText() << " not found";
            token.printLine(os);
            os << std::endl;
            validationFailed_ = true;
        }
    } else if (arg.type == ValueType::DICT
               && arg.second->type == ValueType::STRUCT) {
        // dict<x, target>
        auto& token = arg.second->token;
        if (structSet_.find(token.getText()) == structSet_.end()) {
            os << getTokenError(token, os) << "User defined type: "
               << token.getText() << " not found";
            token.printLine(os);
            os << std::endl;
            validationFailed_ = true;
        }
    } else if (arg.type == ValueType::SEQ
               && arg.first->type == ValueType::STRUCT) {
        auto& token = arg.first->token;
        if (structSet_.find(token.getText()) == structSet_.end()) {
            os << getTokenError(token, os) << "User defined type: "
               << token.getText() << " not found";
            token.printLine(os);
            os << std::endl;
            validationFailed_ = true;
        }
    }
    return true;
}

bool Parser::validateServiceMethod(std::ostream& os, IdlMethod& method) {
    if (method.retType.type == ValueType::STRUCT) {
        auto& token = method.retType.token;
        if (structSet_.find(token.getText()) == structSet_.end()) {
            os << getTokenError(token, os) << "User defined type: "
               << token.getText() << " not found";
            token.printLine(os);
            os << std::endl;
            validationFailed_ = true;
        }
    } else if (method.retType.type == ValueType::DICT
               && method.retType.second->type == ValueType::STRUCT) {
        // dict<x, target>
        auto& token = method.retType.second->token;
        if (structSet_.find(token.getText()) == structSet_.end()) {
            os << getTokenError(token, os) << "User defined type: "
               << token.getText() << " not found";
            token.printLine(os);
            os << std::endl;
            validationFailed_ = true;
        }
    } else if (method.retType.type == ValueType::SEQ
               && method.retType.first->type == ValueType::STRUCT) {
        auto& token = method.retType.first->token;
        if (structSet_.find(token.getText()) == structSet_.end()) {
            os << getTokenError(token, os) << "User defined type: "
               << token.getText() << " not found";
            token.printLine(os);
            os << std::endl;
            validationFailed_ = true;
        }
    }
    for (auto& arg : method.arg_list) {
        validateMethodArgument(os, arg);
    }
    return true;
}

bool Parser::validateAllService(std::ostream& os) {
    for (auto& it : serviceMap_) {
        for (auto& method : it.second.method_list) {
            validateServiceMethod(os, method);
        }
    }
    return true;
}

bool Parser::validate(std::ostream& os) {
    if (!error_.empty()) {
        os << error_;
        return false;
    }
    validateAllStruct(os);
    validateAllService(os);
    return !validationFailed_;
}

Token& Parser::skipBlankNoEof(std::istream& is, Tokenizer& tokenizer) {
    while (true) {
        auto& token = tokenizer.getNext(is);
        switch (token.getType()) {
            case TokenType::TERM_BLANK:
                break;
            case TokenType::TERM_DOUBLE_SLASH:
                doSingleComment(is, tokenizer);
                break;
            case TokenType::TERM_COMMENT_BEGIN:
                doMultiComment(is, tokenizer);
                break;
            default:
                return tokenizer.getCurToken();
        }
    }
}

void Parser::addStruct(IdlStruct& stru) {
    if (structSet_.find(stru.name) != structSet_.end()) {
        throw std::runtime_error("Error, struct already defined");
    }
    structSet_.insert(stru.name);
    structList_.emplace_back(std::move(stru));
}

void Parser::doWrapChunk(std::istream& is, Tokenizer& tokenizer, std::ostream& os,
    Token& cur) {
    tokenizer.unget();
    while (true) {
        if (!doChunk(is, tokenizer, os, false)) {
            cur = tokenizer.getCurToken();
            return;
        }
    }
}

void Parser::doStruct(std::istream& is, Tokenizer& tokenizer, std::ostream& os) {
    if (skipBlankNoEof(is, tokenizer).getType() != TokenType::IDENTIFIER) {
        throw std::runtime_error("Error, need a identifier name");
    }
    IdlStruct idlStruct;
    idlStruct.name = tokenizer.getCurToken().getText();
    if (skipBlankNoEof(is, tokenizer).getType() != TokenType::TERM_LEFT_BRACE) {
        throw std::runtime_error("Error, need a {, do you forgot { for a struct?");
    }
    auto& token = skipBlankNoEof(is, tokenizer);
    while (true) {
        if (token.getType() == TokenType::TERM_RIGHT_BRACE) {
            break;
        }
        doWrapChunk(is, tokenizer, os, token);
        if (token.getType() == TokenType::TERM_RIGHT_BRACE) {
            break;
        }
        auto field = doStructField(is, tokenizer, token);
        if (!idlStruct.addField(std::move(field))) {
            throw std::runtime_error("Error, field name already in used");
        }
        token = skipBlankNoEof(is, tokenizer);
        if (token.getType() == TokenType::TERM_RIGHT_BRACE) {
            break;
        }
    }
    addStruct(idlStruct);
}

ValueType getValueType(Token& cur) {
    switch (cur.getType()) {
        case TokenType::IDENTIFIER:
            return ValueType::STRUCT;
        case TokenType::KEYWORD_SEQ:
            return ValueType::SEQ;
        case TokenType::KEYWORD_SET:
            return ValueType::SET;
        case TokenType::KEYWORD_DICT:
            return ValueType::DICT;
        case TokenType::KEYWORD_I8:
            return ValueType::I8;
        case TokenType::KEYWORD_I16:
            return ValueType::I16;
        case TokenType::KEYWORD_I32:
            return ValueType::I32;
        case TokenType::KEYWORD_I64:
            return ValueType::I64;
        case TokenType::KEYWORD_UI8:
            return ValueType::UI8;
        case TokenType::KEYWORD_UI16:
            return ValueType::UI16;
        case TokenType::KEYWORD_UI32:
            return ValueType::UI32;
        case TokenType::KEYWORD_UI64:
            return ValueType::UI64;
        case TokenType::KEYWORD_FLOAT:
            return ValueType::FLOAT;
        case TokenType::KEYWORD_DOUBLE:
            return ValueType::DOUBLE;
        case TokenType::KEYWORD_BOOL:
            return ValueType::BOOL;
        case TokenType::KEYWORD_STRING:
            return ValueType::STRING;
        case TokenType::KEYWORD_VOID:
            return ValueType::VOID;
        default:
            return ValueType::NONE;
    }
}

void Parser::doFieldIdentifier(std::istream& is, Tokenizer& tokenizer, IdlNode& field,
    bool withName) {
    field.type = ValueType::STRUCT;
    field.token = tokenizer.getCurToken();
    if (withName) {
        auto& cur = skipBlankNoEof(is, tokenizer);
        if (cur.getType() != TokenType::IDENTIFIER) {
            throw std::runtime_error("Error, need a identifier name");
        }
        field.first = new IdlNode();
        field.first->token = cur;
    }
}

void Parser::doFieldSeq(std::istream& is, Tokenizer& tokenizer, IdlNode& field,
    bool withName) {
    field.type = ValueType::SEQ;
    auto& cur = skipBlankNoEof(is, tokenizer);
    if (cur.getType() != TokenType::TERM_TEMP_LEFT) {
        throw std::runtime_error("Error, need a <");
    }
    cur = skipBlankNoEof(is, tokenizer);
    if (field.type == ValueType::NONE) {
        throw std::runtime_error("Error, end of file");
    }
    field.first = new IdlNode();
    // field type
    field.first->token = cur;
    field.first->type = getValueType(cur);
    cur = skipBlankNoEof(is, tokenizer);
    if (cur.getType() != TokenType::TERM_TEMP_RIGHT) {
        throw std::runtime_error("Error, need a >");
    }
    if (withName) {
        cur = skipBlankNoEof(is, tokenizer);
        if (cur.getType() != TokenType::IDENTIFIER) {
            throw std::runtime_error("Error, need a identifier name");
        }
        // field name
        field.token = cur;
    }
}

void Parser::doFieldSet(std::istream& is, Tokenizer& tokenizer, IdlNode& field,
    bool withName) {
    field.type = ValueType::SET;
    auto& cur = skipBlankNoEof(is, tokenizer);
    if (cur.getType() != TokenType::TERM_TEMP_LEFT) {
        throw std::runtime_error("Error, need a <");
    }
    cur = skipBlankNoEof(is, tokenizer);
    if (!cur) {
        throw std::runtime_error("Error, need a identifier name");
    }
    field.first = new IdlNode();
    field.first->type = getValueType(cur);
    field.first->token = cur;
    if (!isContKey(field.first->type)) {
        throw std::runtime_error("Error, cannot be a key of set");
    }
    if (field.first->type == ValueType::NONE) {
        throw std::runtime_error("Error, end of file");
    }
    cur = skipBlankNoEof(is, tokenizer);
    if (cur.getType() != TokenType::TERM_TEMP_RIGHT) {
        throw std::runtime_error("Error, need a >");
    }
    if (withName) {
        cur = skipBlankNoEof(is, tokenizer);
        if (cur.getType() != TokenType::IDENTIFIER) {
            throw std::runtime_error("Error, need a identifier name");
        }
        // field name
        field.token = cur;
    }
}

void Parser::doFieldDict(std::istream& is, Tokenizer& tokenizer, IdlNode& field,
    bool withName) {
    field.type = ValueType::DICT;
    auto& cur = skipBlankNoEof(is, tokenizer);
    if (cur.getType() != TokenType::TERM_TEMP_LEFT) {
        throw std::runtime_error("Error, need a <");
    }
    cur = skipBlankNoEof(is, tokenizer);
    if (!cur) {
        throw std::runtime_error("Error, need a identifier name");
    }
    field.first = new IdlNode();
    field.first->type = getValueType(cur);
    field.first->token = cur;
    if (!isContKey(field.first->type)) {
        throw std::runtime_error("Error, cannot be a key of dict");
    }
    field.first->token = cur;
    if (field.first->type == ValueType::NONE) {
        throw std::runtime_error("Error, end of file");
    }
    cur = skipBlankNoEof(is, tokenizer);
    if (cur.getType() != TokenType::TERM_COMMA) {
        throw std::runtime_error("Error, need a ,");
    }
    field.second = new IdlNode();
    field.second->token = cur;
    cur = skipBlankNoEof(is, tokenizer);
    field.second->type = getValueType(cur);
    if (field.second->type == ValueType::NONE) {
        throw std::runtime_error("Error, end of file");
    }
    field.second->token = cur;
    cur = skipBlankNoEof(is, tokenizer);
    if (cur.getType() != TokenType::TERM_TEMP_RIGHT) {
        throw std::runtime_error("Error, need a >");
    }
    if (withName) {
        cur = skipBlankNoEof(is, tokenizer);
        if (cur.getType() != TokenType::IDENTIFIER) {
            throw std::runtime_error("Error, need a identifier name");
        }
        field.token = cur;
    }
}

IdlNode Parser::doStructField(std::istream& is, Tokenizer& tokenizer, Token& cur) {
    IdlNode field;
    switch (cur.getType()) {
        case TokenType::IDENTIFIER:
            doFieldIdentifier(is, tokenizer, field, true);
            break;
        case TokenType::KEYWORD_SEQ:
            doFieldSeq(is, tokenizer, field, true);
            break;
        case TokenType::KEYWORD_SET:
            doFieldSet(is, tokenizer, field, true);
            break;
        case TokenType::KEYWORD_DICT:
            doFieldDict(is, tokenizer, field, true);
            break;
        default:
            field.type = getValueType(cur);
            cur = skipBlankNoEof(is, tokenizer);
            if (cur.getType() != TokenType::IDENTIFIER) {
                throw std::runtime_error("Error, need a identifier name");
            }
            field.token = cur;
            break;
    }
    return field;
}

IdlServiceNotation Parser::doNotation(std::istream& is, Tokenizer& tokenizer, Token& cur) {
    IdlServiceNotation notation;
    std::string content;
    auto& token = skipBlankNoEof(is, tokenizer);
    while (token && token.getType() != TokenType::TERM_RIGHT_BRACKET) {
        content += token.getText();
        token = skipBlankNoEof(is, tokenizer);
    }
    if (!token) {
        throw std::runtime_error("Error, need a ']', do you forget a ']'?");
    }
    std::vector<std::string> result;
    split(content, ":", result);
    if (!result.empty() && result.size() == 1) {
        notation.key = result[0];
    } else if (!result.empty() && result.size() == 2) {
        notation.key = result[0];
        std::string value = result[1];
        split(value, ",", result);
        for (auto& s : result) {
            notation.values.push_back(s);
        }
        if (result.empty()) {
            notation.values.push_back(value);
        }
    } else {
        throw std::runtime_error("Error, invalid notation format");
    }
    return notation;
}

void Parser::doService(std::istream& is, Tokenizer& tokenizer, std::ostream& os) {
    IdlService service;
    auto& token = skipBlankNoEof(is, tokenizer);
    if (token.getType() == TokenType::KEYWORD_STATIC) {
        service.loadType = ServiceLoadType::STATIC;
        token = skipBlankNoEof(is, tokenizer);
    } else if (token.getType() == TokenType::KEYWORD_DYNAMIC) {
        service.loadType = ServiceLoadType::DYNAMIC;
        token = skipBlankNoEof(is, tokenizer);
    }
    if (token.getType() != TokenType::IDENTIFIER) {
        throw std::runtime_error("Error, need a identifier name, do you mean a service?");
    }
    service.fileName = curFile_;
    service.name = tokenizer.getCurToken().getText();
    service.uuid = frontend_->makeServiceUUID(curFile_, service.name);
    token = skipBlankNoEof(is, tokenizer);
    if (token.getType() == TokenType::KEYWORD_MULTIPLE) {
        service.type = ServiceType::MULTIPLE;
        token = skipBlankNoEof(is, tokenizer);
        if (token.getType() == TokenType::TERM_EQUAL) {
            token = skipBlankNoEof(is, tokenizer);
            // TODO check is number
            service.maxInst = std::stoul(token.getText());
            token = skipBlankNoEof(is, tokenizer);
        }
    } else if (token.getType() == TokenType::KEYWORD_SINGLE) {
        service.type = ServiceType::SINGLE;
        token = skipBlankNoEof(is, tokenizer);
    } else if (token.getType() == TokenType::KEYWORD_REENTRANT) {
        service.type = ServiceType::REENTRANT;
        token = skipBlankNoEof(is, tokenizer);
    } else if (token.getType() == TokenType::KEYWORD_GENERIC) {
        service.type = ServiceType::GENERIC;
        token = skipBlankNoEof(is, tokenizer);
    }
    if (token.getType() != TokenType::TERM_LEFT_BRACE) {
        throw std::runtime_error("Error, need a {");
    }
    token = skipBlankNoEof(is, tokenizer);
    while (true) {
        if (token.getType() == TokenType::TERM_RIGHT_BRACE) {
            break;
        }
        doWrapChunk(is, tokenizer, os, token);
        if (token.getType() == TokenType::TERM_RIGHT_BRACE) {
            serviceMap_.emplace(service.name, std::move(service));
            return;
        }
        if (token.getType() == TokenType::TERM_LEFT_BRACKET) {
            auto notation = doNotation(is, tokenizer, token);
            service.service_notations.emplace(notation.key, notation);
        } else {
            auto method = doMethod(is, tokenizer, token);
            if (!service.addMethod(std::move(method))) {
                throw std::runtime_error("Error, method already defined");
            }
        }
        token = skipBlankNoEof(is, tokenizer);
        if (token.getType() == TokenType::TERM_RIGHT_BRACE) {
            break;
        }
    }
    if (serviceMap_.find(service.name) != serviceMap_.end()) {
        throw std::runtime_error("Error, service already defined");
    }
    serviceMap_.emplace(service.name, std::move(service));
}

void Parser::doPreKeyword(IdlMethod& method, std::istream& is, Tokenizer& tokenizer, Token& cur) {
    while (true) {
        if (cur.getType() == TokenType::KEYWORD_ONEWAY) {
            method.oneway = true;
            cur = skipBlankNoEof(is, tokenizer);
            if (cur.getType() == TokenType::KEYWORD_VOID) {
                return;
            } else if (cur.getType() == TokenType::KEYWORD_NOEXCEPT) {
                continue;
            } else {
                throw std::runtime_error("Error, oneway method must return void or other pre-keyword");
            }
        } else if (cur.getType() == TokenType::KEYWORD_NOEXCEPT) {
            method.no_except = true;
            cur = skipBlankNoEof(is, tokenizer);
            if (cur.getType() == TokenType::KEYWORD_VOID) {
                return;
            } else if(cur.getType() == TokenType::KEYWORD_ONEWAY) {
                continue;
            }
        } else {
            return;
        }
    }    
}

IdlMethod Parser::doMethod(std::istream& is, Tokenizer& tokenizer, Token& cur) {
    IdlMethod method;
    doPreKeyword(method, is, tokenizer, cur);
    method.retType = doMethodRet(is, tokenizer, cur);
    auto& token = skipBlankNoEof(is, tokenizer);
    if (token.getType() != TokenType::IDENTIFIER) {
        throw std::runtime_error("Error, need a identifier name");
    }
    method.name = token.getText();
    token = skipBlankNoEof(is, tokenizer);
    if (token.getType() != TokenType::TERM_LEFT_ROUND) {
        throw std::runtime_error("Error, need a (, do you mean a method?");
    }
    token = skipBlankNoEof(is, tokenizer);
    if (token.getType() == TokenType::TERM_RIGHT_ROUND) {
        return method;
    }
    while (true) {
        auto arg = doMethodArg(is, tokenizer, token);
        method.arg_list.emplace_back(std::move(arg));
        if (tokenizer.getCurToken().getType() == TokenType::TERM_RIGHT_ROUND) {
            break;
        }
        token = skipBlankNoEof(is, tokenizer);
    }
    while (true) {
        token = skipBlankNoEof(is, tokenizer);
        if (token.getType() == TokenType::KEYWORD_TIMEOUT) {
            if (method.oneway) {
                throw std::runtime_error("Error, oneway method no timeout attribute");
            }
            token = skipBlankNoEof(is, tokenizer);
            if (token.getType() != TokenType::TERM_EQUAL) {
                throw std::runtime_error("Error, need a =");
            }
            token = skipBlankNoEof(is, tokenizer);
            if (token.getType() != TokenType::IDENTIFIER) {
                throw std::runtime_error("Error, need timeout in second");
            }
            try {
                method.timeout = std::stoi(token.getText());
            } catch (std::exception&) {
                throw std::runtime_error("Error, timeout must be a number in [0, maximum int]");
            }
            if (method.timeout < 0) {
                throw std::runtime_error("Error, timeout must be a number larger than zero");
            }
        } else if (token.getType() == TokenType::KEYWORD_RETRY) {
            if (method.oneway) {
                throw std::runtime_error("Error, oneway method no retry attribute");
            }
            token = skipBlankNoEof(is, tokenizer);
            if (token.getType() != TokenType::TERM_EQUAL) {
                throw std::runtime_error("Error, need a =");
            }
            token = skipBlankNoEof(is, tokenizer);
            if (token.getType() != TokenType::IDENTIFIER) {
                throw std::runtime_error("Error, need retry count");
            }
            try {
                method.retry = std::stoi(token.getText());
            } catch (std::exception&) {
                throw std::runtime_error("Error, retry must be a number in [0, maximum int]");
            }
            if (method.retry < 0) {
                throw std::runtime_error("Error, retry must be a number larger than zero");
            }
        } else {
            tokenizer.unget();
            break;
        }
    }
    return method;
}

IdlNode Parser::doMethodRet(std::istream& is, Tokenizer& tokenizer, Token& cur) {
    IdlNode retType;
    if (cur.getType() == TokenType::IDENTIFIER) {
        retType.type = ValueType::STRUCT;
        retType.token = cur;
    } else {
        retType.type = getValueType(cur);
        switch (retType.type) {
            case ValueType::SEQ:
                doFieldSeq(is, tokenizer, retType, false);
                break;
            case ValueType::SET:
                doFieldSet(is, tokenizer, retType, false);
                break;
            case ValueType::DICT:
                doFieldDict(is, tokenizer, retType, false);
                break;
            default:
                retType.token = cur;
                break;
        }
    }
    return retType;
}

IdlNode Parser::doMethodArg(std::istream& is, Tokenizer& tokenizer, Token& cur) {
    IdlNode arg;
    if (cur.getType() == TokenType::IDENTIFIER) {
        arg.type = ValueType::STRUCT;
        arg.token = cur;
    } else {
        arg.type = getValueType(cur);
        switch (arg.type) {
            case ValueType::SEQ:
                doFieldSeq(is, tokenizer, arg, false);
                break;
            case ValueType::SET:
                doFieldSet(is, tokenizer, arg, false);
                break;
            case ValueType::DICT:
                doFieldDict(is, tokenizer, arg, false);
                break;
            default:
                arg.token = cur;
                break;
        }
    }
    auto& token = skipBlankNoEof(is, tokenizer);
    if (token.getType() == TokenType::TERM_COMMA) {
        return arg;
    } else if (token.getType() == TokenType::TERM_RIGHT_ROUND) {
        return arg;
    } else {
        throw std::runtime_error("Error, need a , or )");
    }
}

void Parser::doSingleComment(std::istream& is, Tokenizer& tokenizer) {
    tokenizer.ignore();
    while (true) {
        auto& token = tokenizer.getNext(is);
        if (!token) {
            tokenizer.normal();
            return;
        }
        if ((token.getType() == TokenType::TERM_BLANK) && (token.getText() == "\n")) {
            tokenizer.normal();
            return;
        }
    }
    tokenizer.normal();
}

void Parser::doMultiComment(std::istream& is, Tokenizer& tokenizer) {
    tokenizer.ignore();
    while (true) {
        auto& token = tokenizer.getNext(is);
        if (!token) {
            throw std::runtime_error("Error, end of file");
        }
        if (token.getType() == TokenType::TERM_COMMENT_END) {
            tokenizer.normal();
            return;
        }
    }
    tokenizer.normal();
}

void Parser::doImport(std::istream& is, Tokenizer& tokenizer, std::ostream& os) {
    tokenizer.ignore();
    auto& token = tokenizer.getNext(is);
    if (token.getType() != TokenType::KEYWORD_IMPORT) {
        throw std::runtime_error("Error, need import");
    }
    token = skipBlankNoEof(is, tokenizer);
    if (token.getType() != TokenType::TERM_QUOTE) {
        throw std::runtime_error("Error, need a \"");
    }
    std::string name;
    while (true) {
        tokenizer.ignore();
        token = tokenizer.getNext(is);
        if (token.getType() == TokenType::TERM_QUOTE) {
            tokenizer.normal();
            break;
        } else {
            name += token.getText();
        }
    }
    // TODO 检查是否真的是同一个文件
    auto it = fileSet_.find(name);
    if (it != fileSet_.end()) {
        throw std::runtime_error("Error, multi-import");
    } else {
        fileSet_.emplace(name);
    }
    std::string temp = curFile_;
    Tokenizer* last = curTokenizer_;

    bool success = false;
    for (const std::string& searchpath : includePathlist_) {
        if (analyze(name, os, searchpath)) {
            success = true;
            break;
        }
    }

    //如果最后都没有成功 那就抛异常
    if (!success) {
        error_ = "file " + name + " not found\n";
        throw 1;
    }

    curFile_ = temp;
    curTokenizer_ = last;
    tokenizer.normal();
}
